package com.hrzyit.whistle.im.widgets

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Handler
import android.text.Spannable
import android.text.SpannableString
import android.text.style.ImageSpan
import android.util.AttributeSet
import android.widget.TextView
import com.hrzyit.base.utils.IMUtils
import com.hrzyit.whistle.im.util.GifOpenHelper
import java.lang.ref.WeakReference
import java.util.*
import java.util.regex.Pattern

class GifTextView : TextView {

    /**
     * spanInfoList 是一个SpanInfo的list ,用于处理一个TextView中出现多个要匹配的图片的情况
     */
    private var spanInfoList: ArrayList<SpanInfo>? = null
    private var gifHandler: Handler? = null // 用于处理从子线程TextView传来的消息
    private var myText: String? = null // 存储textView应该显示的文本

    private var isGif: Boolean = false

    lateinit var rTextRunnable: TextRunnable

    /**
     * @author Dragon SpanInfo
     * 类用于存储一个要显示的图片（动态或静态）的信息，包括分解后的每一帧mapList、替代文字的起始位置、终止位置
     * 、帧的总数、当前需要显示的帧、帧与帧之间的时间间隔
     */
    private inner class SpanInfo {
        internal var mapList: ArrayList<Bitmap>
        internal var start: Int = 0
        internal var end: Int = 0
        internal var frameCount: Int = 0
        internal var currentFrameIndex: Int = 0
        internal var delay: Int = 0

        init {
            mapList = ArrayList()
            delay = 0
            currentFrameIndex = delay
            frameCount = currentFrameIndex
            end = frameCount
            start = end
        }
    }

    /**
     * 这三个构造方法一个也不要少，否则会产生CastException，注意在这三个构造函数中都为spanInfoList实例化，可能有些浪费
     * ，但保证不会有空指针异常
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    @SuppressLint("NewApi")
    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
        this@GifTextView.isFocusableInTouchMode = false
    }

    @SuppressLint("NewApi")
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        this@GifTextView.isFocusableInTouchMode = false
    }

    @SuppressLint("NewApi")
    constructor(context: Context) : super(context) {
        this@GifTextView.isFocusableInTouchMode = false
    }

    /**
     * 对要显示在textView上的文本进行解析，看看是否文本中有需要与Gif或者静态图片匹配的文本 若有，那么调用parseGif
     * 对该文本对应的Gif图片进行解析 或者调用parseBmp解析静态图片
     *
     * @param inputStr
     */
    private fun parseText(inputStr: String?): Boolean {
        myText = inputStr
        val regexEmotion = "\\[([\u4e00-\u9fa5\\w])+\\]"
        val patternEmotion = Pattern.compile(regexEmotion)
        val mMatcher = patternEmotion.matcher(inputStr)

        var hasGif = false
//        while (mMatcher.find()) {
//            val faceName = mMatcher.group()
//            var faceId: Int? = null
//            /**
//             * 这里匹配时用到了图片库，即一个专门存放图片id和其匹配的名称的静态对象，这两个静态对象放在了FaceData.java
//             * 中，并采用了静态块的方法进行了初始化，不会有空指针异常
//             */
//            if ((faceId = EmotionUtils.EMPTY_GIF_MAP.get(faceName)) != null) {
//                if (isGif) {
//                    parseGif(faceId!!, mMatcher.start(), mMatcher.end())
//                } else {
//                    parseBmp(faceId!!, mMatcher.start(), mMatcher.end())
//                }
//            }
//            hasGif = true
//        }
        return hasGif
    }

    /**
     * 对静态图片进行解析：
     * 创建一个SpanInfo对象，帧数设为1，按照下面的参数设置，最后不要忘记将SpanInfo对象添加进spanInfoList中， 否则不会显示
     *
     * @param resourceId
     * @param start
     * @param end
     */
    private fun parseBmp(resourceId: Int, start: Int, end: Int) {
        val bitmap = BitmapFactory.decodeResource(
            context
                .resources, resourceId
        )
        val imageSpan = ImageSpan(context, bitmap)
        val spanInfo = SpanInfo()
        spanInfo.currentFrameIndex = 0
        spanInfo.frameCount = 1
        spanInfo.start = start
        spanInfo.end = end
        spanInfo.delay = 100
        spanInfo.mapList.add(bitmap)
        spanInfoList!!.add(spanInfo)

    }

    /**
     * 解析Gif图片，与静态图片唯一的不同是这里需要调用GifOpenHelper类读取Gif返回一系一组bitmap（用for 循环把这一
     * 组的bitmap存储在SpanInfo.mapList中，此时的frameCount参数也大于1了）
     *
     * @param resourceId
     * @param start
     * @param end
     */
    private fun parseGif(resourceId: Int, start: Int, end: Int) {

        val helper = GifOpenHelper()
        helper.read(context.resources.openRawResource(resourceId))
        val spanInfo = SpanInfo()
        spanInfo.currentFrameIndex = 0
        spanInfo.frameCount = helper.frameCount
        spanInfo.start = start
        spanInfo.end = end
        spanInfo.mapList.add(helper.bitMapImage)
        for (i in 1 until helper.frameCount) {
            spanInfo.mapList.add(helper.nextBitmap())
        }
        spanInfo.delay = helper.nextDelay() // 获得每一帧之间的延迟
        spanInfoList!!.add(spanInfo)

    }

    /**
     * GifTextView 与外部对象的接口，以后设置文本内容时使用setSpanText() 而不再是setText();
     *
     * @param handler
     * @param text
     */
    fun setSpanText(handler: Handler, text: String?, isGif: Boolean) {
        this.gifHandler = handler // 获得UI的Handler
        this.isGif = isGif
        spanInfoList = ArrayList()
        if (parseText(text)) {// 对String对象进行解析
            if (parseMessage(this)) {
                startPost()
            }
        } else {
            setText(myText)
        }
    }

    fun parseMessage(gifTextView: GifTextView): Boolean {
        if (gifTextView.myText != null && gifTextView.myText != "") {
            val sb = SpannableString("" + gifTextView.myText!!) // 获得要显示的文本
            var gifCount = 0
            var info: SpanInfo?
            for (i in gifTextView.spanInfoList!!.indices) { // for循环，处理显示多个图片的问题
                info = gifTextView.spanInfoList!![i]
                if (info.mapList.size > 1) {
                    /*
                             * gifCount用来区分是Gif还是BMP，若是gif gifCount>0
							 * ,否则gifCount=0
							 */
                    gifCount++

                }
                var bitmap = info.mapList[info.currentFrameIndex]
                info.currentFrameIndex = (info.currentFrameIndex + 1) % info.frameCount
                /**
                 * currentFrameIndex
                 * 用于控制当前应该显示的帧的序号，每次显示之后currentFrameIndex 应该加1
                 * ，加到frameCount后再变成0循环显示
                 */
                val size = IMUtils.dp2px(gifTextView.context, 30f)
                if (gifCount != 0) {
                    bitmap = Bitmap.createScaledBitmap(
                        bitmap, size,
                        size, true
                    )

                } else {
                    bitmap = Bitmap.createScaledBitmap(
                        bitmap, size,
                        size, true
                    )
                }
                val imageSpan = ImageSpan(
                    gifTextView.context,
                    bitmap
                )
                if (info.end <= sb.length) {
                    sb.setSpan(
                        imageSpan, info.start, info.end,
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                    )
                } else {
                    break
                }

            }
            // 对所有的图片对应的ImageSpan完成设置后，调用TextView的setText方法设置文本
            gifTextView.text = sb
            return gifCount != 0
        }
        return false
    }

    fun startPost() {
        rTextRunnable = TextRunnable(this) // 生成Runnable对象
        gifHandler!!.post(rTextRunnable) // 利用UI线程的Handler 将r添加进消息队列中。
    }

    class TextRunnable(f: GifTextView) : Runnable {
        private val mWeakReference: WeakReference<GifTextView>

        init {
            mWeakReference = WeakReference(f)
        }

        override fun run() {
            val gifTextView = mWeakReference.get()
            if (gifTextView != null) {
                /**
                 * 这一步是为了节省内存而是用，即如果文本中只有静态图片没有动态图片，那么该线程就此终止，不会重复执行
                 * 。而如果有动图，那么会一直执行
                 */
                if (gifTextView.parseMessage(gifTextView)) {
                    gifTextView.gifHandler!!.postDelayed(this, DELAYED.toLong())
                }
            }
        }
    }

    companion object {
        /**
         * 注：如果获取的gif帧与帧之间的时间间隔都不相同，建议调个固定的，最好的方法是将gif图的间隔设置相同
         */
        private val DELAYED = 600
    }

}

